// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "mlink.h"
#include "mupgrade.h"
#include "mlink_handle.h"

#define MLINK_RESTART_DELAY_TIME_MS (5000)
#define MLINK_HANDLES_MAX_SIZE      (64)
#define CHARACTERISTICS_MAX_NUM     (32)
#define MLINK_DEVICE_NAME_KEY       "ML_NAME"
#define MLINK_DEVICE_POSITION_KEY   "ML_POSITION"
#ifdef CONFIG_DEVICE_MANUFACTURER
#define MLINK_DEVICE_MANUFACTURER CONFIG_DEVICE_MANUFACTURER
#else
#define MLINK_DEVICE_MANUFACTURER "CHENGVIN"

#endif

/**
 * @brief Type of handler list
 */
typedef struct {
    const char *name;         /**< The name of the function */
    mlink_handle_func_t func; /**< The pointer of the function */
} mlink_handle_t;

static const char *TAG               = "mlink_handle";
static mlink_device_t *g_device_info = NULL;
mlink_characteristic_func_t mlink_device_get_value = NULL;
static mlink_characteristic_func_t mlink_device_set_value = NULL;

extern void device_set_mesh_addr(uint8_t * addr);
extern void device_get_mesh_addr(uint8_t * addr);
extern void device_set_default_netif_name(char *netifkey);
extern void device_get_default_netif_name(char *netifkey);
extern void device_set_sn(char *sn);
extern char *device_get_sn(char *sn);

mdf_err_t mlink_add_device(uint32_t tid, const char *name, const char *version)
{
    MDF_PARAM_CHECK(name);
    MDF_PARAM_CHECK(version);

    if (!g_device_info) {
        g_device_info = MDF_MALLOC(sizeof(mlink_device_t));
        MDF_ERROR_CHECK(!g_device_info, MDF_ERR_NO_MEM, "");
    }

    memset(g_device_info, 0, sizeof(mlink_device_t));

    if (__mdf_info_load(MLINK_DEVICE_NAME_KEY, g_device_info->name, sizeof(g_device_info->name), LENGTH_TYPE_NUMBER) != MDF_OK) {
        strncpy(g_device_info->name, name, sizeof(g_device_info->name) - 1);
    }

    __mdf_info_load(MLINK_DEVICE_POSITION_KEY, g_device_info->position, sizeof(g_device_info->position), LENGTH_TYPE_NUMBER);

    g_device_info->tid = tid;
    strncpy(g_device_info->version, version, sizeof(g_device_info->version) - 1);

    g_device_info->characteristics_num = 0;
    g_device_info->characteristics     = NULL;

    return MDF_OK;
}

mdf_err_t mlink_device_set_name(const char *name)
{
    MDF_PARAM_CHECK(g_device_info);
    MDF_PARAM_CHECK(name);

    mdf_err_t ret = MDF_OK;

    strncpy(g_device_info->name, name, sizeof(g_device_info->name) - 1);
    ret = mdf_info_save(MLINK_DEVICE_NAME_KEY, g_device_info->name,
                        sizeof(g_device_info->name));
    MDF_ERROR_CHECK(ret < 0, MDF_FAIL, "mdf_info_save, ret: %d", ret);

    return MDF_OK;
}

const char *mlink_device_get_name()
{
    if (!g_device_info) {
        return NULL;
    }

    return g_device_info->name;
}

mdf_err_t mlink_device_set_position(const char *position)
{
    MDF_PARAM_CHECK(g_device_info);
    MDF_PARAM_CHECK(position);

    mdf_err_t ret = MDF_OK;

    strncpy(g_device_info->position, position, sizeof(g_device_info->position) - 1);

    ret = mdf_info_save(MLINK_DEVICE_POSITION_KEY, g_device_info->position,
                        sizeof(g_device_info->position));
    MDF_ERROR_CHECK(ret < 0, MDF_FAIL, "mdf_info_save, ret: %d", ret);

    return MDF_OK;
}

const char *mlink_device_get_position()
{
    if (!g_device_info) {
        return NULL;
    }

    return g_device_info->position;
}

const char *mlink_device_get_version()
{
    if (!g_device_info) {
        return NULL;
    }

    return g_device_info->version;
}

int mlink_device_get_tid()
{
    MDF_PARAM_CHECK(g_device_info);

    return g_device_info->tid;
}

mdf_err_t mlink_add_characteristic(char *cid, const char *name, characteristic_format_t format,
                                   characteristic_perms_t perms, int min_inr, int max_inr, float change_val)
{
    MDF_PARAM_CHECK(g_device_info);
    MDF_PARAM_CHECK(name);

    g_device_info->characteristics = MDF_REALLOC(g_device_info->characteristics,
                                     (g_device_info->characteristics_num + 1) * sizeof(mlink_characteristics_t));
    MDF_ERROR_CHECK(!g_device_info->characteristics, MDF_ERR_NO_MEM, "");
    mlink_characteristics_t *characteristics = g_device_info->characteristics + g_device_info->characteristics_num;

    memset(characteristics->cid, 0, sizeof(characteristics->cid));
    strncpy(characteristics->cid, cid, sizeof(characteristics->cid) - 1);
    characteristics->format = format;
    characteristics->perms  = perms;
    // characteristics->min    = min;
    // characteristics->max    = max;
    // characteristics->step   = step;
    characteristics->max_inr = max_inr;
    characteristics->change_val = change_val;
    characteristics->update_time = 0;
    characteristics->report_time = 0;
    // characteristics->min_inr = min_inr;
    g_device_info->characteristics_num++;

    // memset(characteristics->name, 0, sizeof(characteristics->name));
    // strncpy(characteristics->name, name, sizeof(characteristics->name) - 1);

    return MDF_OK;
}

characteristic_format_t mlink_get_characteristics_format(char *cid)
{
    mlink_characteristics_t *characteristic = g_device_info->characteristics;

    for (int i = 0; i < g_device_info->characteristics_num; ++i) {
        if (strcmp(characteristic[i].cid, cid) == 0 ) {
            return characteristic[i].format;
        }
    }

    return CHARACTERISTIC_FORMAT_NONE;
}

characteristic_perms_t mlink_get_characteristics_perms(char *cid)
{
    // mlink_characteristics_t *characteristic = g_device_info->characteristics;

    // for (int i = 0; i < g_device_info->characteristics_num; ++i) {
    //     if (strcmp(characteristic[i].cid, cid) == 0 ) {
    //         return (characteristic_perms_t)characteristic[i].perms;
    //     }
    // }

    // return (characteristic_perms_t)0;

    return CHARACTERISTIC_PERMS_RWT;
}

mlink_characteristics_t *mlink_get_characteristics_info(char *cid)
{
    mlink_characteristics_t *characteristic = g_device_info->characteristics;

    for (int i = 0; i < g_device_info->characteristics_num; ++i) {
        if (strcmp(characteristic[i].cid, cid) == 0 ) {
            return &(characteristic[i]);
        }
    }

    return NULL;
}

mdf_err_t mlink_add_characteristic_handle(mlink_characteristic_func_t get_value_func, mlink_characteristic_func_t set_value_func)
{
    MDF_PARAM_CHECK(get_value_func);

    mdf_err_t ret = MDF_OK;
    uint16_t group_num = 0;

    mlink_device_get_value = get_value_func;
    mlink_device_set_value = set_value_func;

    if (__mdf_info_load("group_num", &group_num, sizeof(ssize_t), LENGTH_TYPE_NUMBER) == MDF_OK) {
        mesh_addr_t *group_list = MDF_MALLOC(sizeof(mesh_addr_t) * group_num);
        MDF_ERROR_CHECK(!group_list, MDF_ERR_NO_MEM, "");

        __mdf_info_load("group_list", group_list, sizeof(mesh_addr_t) * group_num, LENGTH_TYPE_NUMBER);

        // ret = esp_mesh_set_group_id((mesh_addr_t *)group_list, group_num);
        // MDF_ERROR_CHECK(ret != MDF_OK, ret, "<%s> Set group ID addresses, group_num: %d",
        //                 mdf_err_to_name(ret), group_num);
    }

    return MDF_OK;
}

static mdf_err_t mlink_handle_system_reboot(mlink_handle_data_t *handle_data)
{
    int delay_time = MLINK_RESTART_DELAY_TIME_MS;
    mlink_json_parse(handle_data->req_data, "delay", &delay_time);

    mdf_err_t ret = mdf_event_loop_delay_send(MDF_EVENT_MLINK_SYSTEM_REBOOT, NULL, pdMS_TO_TICKS(delay_time));
    MDF_ERROR_CHECK(ret < 0, MDF_FAIL, "mdf_event_loop_delay_send, ret: %d", ret);

    return MDF_OK;
}

static mdf_err_t mlink_handle_system_reset(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret  = 0;
    int delay_time = MLINK_RESTART_DELAY_TIME_MS;
    mlink_json_parse(handle_data->req_data, "delay", &delay_time);

    ret = mdf_event_loop_delay_send(MDF_EVENT_MLINK_SYSTEM_RESET, NULL, pdMS_TO_TICKS(delay_time));
    MDF_ERROR_CHECK(ret < 0, MDF_FAIL, "mdf_event_loop_delay_send, ret: %d", ret);

    return MDF_OK;
}

static mdf_err_t mlink_handle_get_info(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret                = MDF_OK;
    char tmp_str[64]             = {0};
    char position[32]            = {0x0};
    size_t position_len          = sizeof(position);
    handle_data->resp_data       = NULL;
    char *characteristics_list   = NULL;
    characteristic_value_t value = {0};
    // uint8_t parent_mac[6]        = {0};
    uint8_t self_mac[6]          = {0};
    uint8_t mesh_id[6]      = {0};

    mlink_characteristics_t *characteristic = g_device_info->characteristics;

    if (__mdf_info_load(MLINK_DEVICE_POSITION_KEY, position, &position_len, LENGTH_TYPE_POINTER) == MDF_OK) {
        mlink_json_pack(&handle_data->resp_data, "position", position);
    }

    sprintf(tmp_str, "%"PRIu32, g_device_info->tid);

    esp_wifi_get_mac(ESP_IF_WIFI_STA, self_mac);
    device_get_mesh_addr(mesh_id);

	mlink_json_pack(&handle_data->resp_data, "mfrs", MLINK_DEVICE_MANUFACTURER);
    mlink_json_pack(&handle_data->resp_data, "tid", tmp_str);
    mlink_json_pack(&handle_data->resp_data, "name", g_device_info->name);
    mlink_json_pack(&handle_data->resp_data, "sn", device_get_sn(NULL));
    mlink_json_pack(&handle_data->resp_data, "self_mac", mlink_mac_hex2str(self_mac, tmp_str));
	mlink_json_pack(&handle_data->resp_data, "mesh_id", mlink_mac_hex2str(mesh_id, tmp_str));
    mlink_json_pack(&handle_data->resp_data, "version", g_device_info->version);
    mlink_json_pack(&handle_data->resp_data, "idf_version", esp_get_idf_version());
    mlink_json_pack(&handle_data->resp_data, "mlink_version", 2);
    mlink_json_pack(&handle_data->resp_data, "mlink_trigger", mlink_trigger_get_num());
	mlink_json_pack(&handle_data->resp_data, "mlink_scenes", mlink_scenes_get_num());

    esp_netif_t *netif = NULL;
    // Ethernet info
    netif = esp_netif_get_handle_from_ifkey("ETH_WAN");
    if(netif) {
		esp_netif_ip_info_t ip_info;
		ret = esp_netif_get_ip_info(netif, &ip_info);
		if(ret == MDF_OK){
			char ipstr[16];
            char *tmp_str = NULL;
            sprintf(ipstr, IPSTR, IP2STR(&ip_info.ip));
			mlink_json_pack(&tmp_str, "ip", ipstr);
            sprintf(ipstr, IPSTR, IP2STR(&ip_info.netmask));
            mlink_json_pack(&tmp_str, "netmask", ipstr);
            sprintf(ipstr, IPSTR, IP2STR(&ip_info.gw));
            mlink_json_pack(&tmp_str, "gw", ipstr);
            mlink_json_pack(&handle_data->resp_data, "eth", tmp_str);
            MDF_FREE(tmp_str);
		}
    }

    netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
    if(netif) {
		esp_netif_ip_info_t ip_info;
		ret = esp_netif_get_ip_info(netif, &ip_info);
		if(ret == MDF_OK){
			char ipstr[16];
            char *tmp_str = NULL;
            sprintf(ipstr, IPSTR, IP2STR(&ip_info.ip));
			mlink_json_pack(&tmp_str, "ip", ipstr);
            sprintf(ipstr, IPSTR, IP2STR(&ip_info.netmask));
            mlink_json_pack(&tmp_str, "netmask", ipstr);
            sprintf(ipstr, IPSTR, IP2STR(&ip_info.gw));
            mlink_json_pack(&tmp_str, "gw", ipstr);

            wifi_config_t wifi_conf;
            ret = esp_wifi_get_config(ESP_IF_WIFI_STA, &wifi_conf);
            if(ret == MDF_OK){
                mlink_json_pack(&tmp_str, "ssid", (char *)wifi_conf.sta.ssid);
                mlink_json_pack(&tmp_str, "password", (char *)wifi_conf.sta.password);
            }
            mlink_json_pack(&handle_data->resp_data, "wifi_sta", tmp_str);
            MDF_FREE(tmp_str);
        }
    }

    for (int i = 0; i < g_device_info->characteristics_num; ++i) {
        char *tmp_str = NULL;

        switch (characteristic[i].format) {
            case CHARACTERISTIC_FORMAT_INT:
                ret = mlink_device_get_value(characteristic[i].cid, &value.value_int);
                MDF_ERROR_BREAK(ret < 0, "<%s> mlink_device_get_value", mdf_err_to_name(ret));
                mlink_json_pack(&tmp_str, "cid", characteristic[i].cid);
                mlink_json_pack(&tmp_str, "value", value.value_int);
                // mlink_json_pack(&tmp_str, "type", "i32");
                // mlink_json_pack(&tmp_str, "max_inr", characteristic[i].max_inr);
                // mlink_json_pack_double(&tmp_str, "change_val", characteristic[i].change_val);
                break;

            case CHARACTERISTIC_FORMAT_DOUBLE:
                ret = mlink_device_get_value(characteristic[i].cid, &value.value_float);
                MDF_ERROR_BREAK(ret < 0, "<%s> mlink_device_get_value", mdf_err_to_name(ret));
                mlink_json_pack(&tmp_str, "cid", characteristic[i].cid);
                mlink_json_pack_double(&tmp_str, "value", value.value_float);
                // mlink_json_pack(&tmp_str, "type", "float");
                // mlink_json_pack(&tmp_str, "max_inr", characteristic[i].max_inr);
                // mlink_json_pack_double(&tmp_str, "change_val", characteristic[i].change_val);
                break;

            case CHARACTERISTIC_FORMAT_STRING:
            case CHARACTERISTIC_FORMAT_ARRAY:
                ret = mlink_device_get_value(characteristic[i].cid, &value.value_string);
                MDF_ERROR_BREAK(ret < 0, "<%s> mlink_device_get_value", mdf_err_to_name(ret));
                mlink_json_pack(&tmp_str, "cid", characteristic[i].cid);
                mlink_json_pack(&tmp_str, "value", value.value_string);
                // mlink_json_pack(&tmp_str, "type", "string");
                // mlink_json_pack(&tmp_str, "max_inr", characteristic[i].max_inr);
                // mlink_json_pack_double(&tmp_str, "change_val", characteristic[i].change_val);
                break;

            default:
                MDF_LOGW("Data types in this format are not supported cid %s", characteristic[i].cid);
                break;

        }

        if (tmp_str) {
            mlink_json_pack(&characteristics_list, "[]", tmp_str);
            MDF_FREE(tmp_str);
        }

    }

    handle_data->resp_size = mlink_json_pack(&handle_data->resp_data, "characteristics", characteristics_list);
    MDF_FREE(characteristics_list);

    return MDF_OK;
}

static mdf_err_t mlink_handle_get_status(mlink_handle_data_t *handle_data)
{
    MDF_ERROR_CHECK(!mlink_device_get_value, MDF_FAIL, "this device does not support get_status");

    mdf_err_t ret                     = MDF_OK;
    int cids_num                      = 0;
    handle_data->resp_data            = NULL;
    char *characteristics_list        = NULL;
    char *cids[CHARACTERISTICS_MAX_NUM] = {0};
    characteristic_value_t value      = {0};

    ret = mlink_json_parse(handle_data->req_data, "cids", &cids_num);
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "Parse the json formatted string");

    ret = mlink_json_parse(handle_data->req_data, "cids", cids);
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "Parse the json formatted string");

    mdf_event_loop_send(MDF_EVENT_MLINK_GET_STATUS, NULL);

    for (int i = 0; i < cids_num; ++i) {
        char *tmp_str = NULL;

        switch (mlink_get_characteristics_format(cids[i])) {
            case CHARACTERISTIC_FORMAT_INT:
                ret = mlink_device_get_value(cids[i], &value.value_int);
                MDF_ERROR_BREAK(ret < 0, "<%s> mlink_device_get_value", mdf_err_to_name(ret));
                mlink_json_pack(&tmp_str, "cid", cids[i]);
                mlink_json_pack(&tmp_str, "value", value.value_int);
                break;

            case CHARACTERISTIC_FORMAT_DOUBLE:
                ret = mlink_device_get_value(cids[i], &value.value_float);
                MDF_ERROR_BREAK(ret < 0, "<%s> mlink_device_get_value", mdf_err_to_name(ret));
                mlink_json_pack(&tmp_str, "cid", cids[i]);
                mlink_json_pack_double(&tmp_str, "value", value.value_float);
                break;

            case CHARACTERISTIC_FORMAT_STRING:
            case CHARACTERISTIC_FORMAT_ARRAY:
                ret = mlink_device_get_value(cids[i], &value.value_string);
                MDF_ERROR_BREAK(ret < 0, "<%s> mlink_device_get_value", mdf_err_to_name(ret));
                mlink_json_pack(&tmp_str, "cid", cids[i]);
                mlink_json_pack(&tmp_str, "value", value.value_string);
                break;

            default:
                MDF_LOGW("Data types in this format are not supported cid %s", cids[i]);
                break;

        }

        if (tmp_str) {
            mlink_json_pack(&characteristics_list, "[]", tmp_str);
            MDF_FREE(tmp_str);
        }
        MDF_FREE(cids[i]);
    }

    MDF_ERROR_CHECK(!characteristics_list, MDF_FAIL, "Create a json string");

    handle_data->resp_size = mlink_json_pack(&handle_data->resp_data,
                             "characteristics", characteristics_list);
    MDF_FREE(characteristics_list);

    return MDF_OK;
}


static void delay_exec_timer_cb(TimerHandle_t timer)
{
    mlink_handle_data_t *arg = (mlink_handle_data_t *)pvTimerGetTimerID(timer);
	xQueueSend(g_mlink_handle_queue, &arg, portMAX_DELAY);
    xTimerDelete(timer, 0);
}

static mdf_err_t mlink_handle_delay_exec(mlink_handle_data_t *handle_data) {
    int ret;
    int delay;
    ret = mlink_json_parse(handle_data->req_data, "delay",	&delay);
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "Parse the json formatted string");

    char *execute_content;
    ret = mlink_json_parse(handle_data->req_data, "execute_content", &execute_content);
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "Parse the json formatted string");

	mlink_handle_data_t *handle_data_copy = MDF_CALLOC(1, sizeof(mlink_handle_data_t));
	handle_data_copy->req_data = execute_content;
	handle_data_copy->req_size	  = strlen(execute_content);
	handle_data_copy->req_fromat  = MLINK_HTTPD_FORMAT_JSON;
	handle_data_copy->resp_fromat = MLINK_HTTPD_FORMAT_JSON;

    TimerHandle_t timer = xTimerCreate("delay_exec", pdMS_TO_TICKS(delay), false, handle_data_copy, delay_exec_timer_cb);
    xTimerStart(timer, 0);

    return ret;

}

static mdf_err_t mlink_handle_set_status(mlink_handle_data_t *handle_data)
{
    MDF_ERROR_CHECK(!mlink_device_set_value, MDF_FAIL, "This device does not support set_status");

    char cid[32];
	// int delay;
    mdf_err_t ret   = MDF_OK;
    int cids_num = 0;
    characteristic_value_t value = {0};
    char *characteristics_list[CHARACTERISTICS_MAX_NUM] = {NULL};

    ret = mlink_json_parse(handle_data->req_data, "characteristics", &cids_num);
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "Parse the json formatted string");

    ret = mlink_json_parse(handle_data->req_data, "characteristics", characteristics_list);
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "Parse the json formatted string");

    for (int i = 0; i < cids_num; ++i) {
        ret = mlink_json_parse(characteristics_list[i], "cid",  cid);

        if (ret) {
            MDF_LOGW("<%s> Parse the json formatted string", mdf_err_to_name(ret));
            MDF_FREE(characteristics_list[i]);
            continue;
        }

        switch (mlink_get_characteristics_format(cid)) {
            case CHARACTERISTIC_FORMAT_INT:
                ret = mlink_json_parse(characteristics_list[i], "value", &value.value_int);
                MDF_ERROR_BREAK(ret != MDF_OK, "<%s> Parse the json formatted string", mdf_err_to_name(ret));
                ret = mlink_device_set_value(cid, &value.value_int);
                MDF_ERROR_BREAK(ret != MDF_OK, "<%s> mlink_device_set_value, cid: %s, value: %d", mdf_err_to_name(ret), cid, value.value_int);
                break;

            case CHARACTERISTIC_FORMAT_DOUBLE:
                ret = mlink_json_parse(characteristics_list[i], "value", &value.value_float);
                MDF_ERROR_BREAK(ret != MDF_OK, "<%s> Parse the json formatted string", mdf_err_to_name(ret));
                ret = mlink_device_set_value(cid, &value.value_float);
                MDF_ERROR_BREAK(ret != MDF_OK, "<%s> mlink_device_set_value, cid: %s, value: %f", mdf_err_to_name(ret), cid, value.value_float);
                break;

            case CHARACTERISTIC_FORMAT_STRING:
                ret = mlink_json_parse(characteristics_list[i], "value", &value.value_string);
                MDF_ERROR_BREAK(ret != MDF_OK, "<%s> Parse the json formatted string", mdf_err_to_name(ret));
                ret = mlink_device_set_value(cid, value.value_string);
                MDF_FREE(value.value_string);
                MDF_ERROR_BREAK(ret != MDF_OK, "<%s> mlink_device_set_value,", mdf_err_to_name(ret));
                break;

            case CHARACTERISTIC_FORMAT_ARRAY:{
                char *array[5];        /**< The item's array */
                int array_num;       /**< The number of the item's array */
                ret = mlink_json_parse(characteristics_list[i], "value", &array_num);
                ret = mlink_json_parse(characteristics_list[i], "value", array);
                MDF_ERROR_BREAK(ret != MDF_OK, "<%s> Parse the json formatted string", mdf_err_to_name(ret));
                ret = mlink_device_set_value(cid, array);
                for(int j=0;j<array_num;j++){// 释放value_array申请的内存
                     MDF_FREE(array[j]);
                }
                MDF_ERROR_BREAK(ret != MDF_OK, "<%s> mlink_device_set_value,", mdf_err_to_name(ret));
                break;
            }

            default:
                MDF_LOGW("Data types in this format are not supported");
                break;
        }

        MDF_FREE(characteristics_list[i]);
    }

    mdf_event_loop_send(MDF_EVENT_MLINK_SET_STATUS, NULL);

    return MDF_OK;
}

static mdf_err_t mlink_handle_set_name(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret = MDF_OK;
    char name[32] = {0};

    ret = mlink_json_parse(handle_data->req_data, "name", name);
    MDF_ERROR_CHECK(ret < 0, ret, "mlink_json_parse");

    ret = mlink_device_set_name(name);
    MDF_ERROR_CHECK(ret < 0, ret, "device_config_set");

    MDF_LOGD("Set device name: %s", name);

    return MDF_OK;
}

static mdf_err_t mlink_handle_set_position(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret     = MDF_OK;
    char position[32] = {0};

    ret = mlink_json_parse(handle_data->req_data, "position", position);
    MDF_ERROR_CHECK(ret < 0, ret, "mlink_json_parse");

    ret = mlink_device_set_position(position);
    MDF_ERROR_CHECK(ret < 0, ret, "device_config_set");

    MDF_LOGD("Set device position: %s", position);

    return MDF_OK;
}

static mdf_err_t mlink_handle_get_ota_progress(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret            = MDF_OK;
    mupgrade_status_t status = {0x0};

    ret = mupgrade_get_status(&status);
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "mupgrade_get_status");

    mlink_json_pack(&handle_data->resp_data, "firmware_name", status.name);
    mlink_json_pack(&handle_data->resp_data, "total_size", status.total_size);
    mlink_json_pack(&handle_data->resp_data, "written_size", status.written_size);

    handle_data->resp_size = strlen(handle_data->resp_data);

    return MDF_OK;
}

static mdf_err_t mlink_handle_set_ota_fallback(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret = MDF_OK;

    ret = mupgrade_version_fallback();
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "Upgrade error back to previous version");

    return mlink_handle_system_reboot(handle_data);
}

static mdf_err_t mlink_handle_get_config(mlink_handle_data_t *handle_data)
{
    uint8_t mesh_id[6]      = {0};
    char temp_str[17]         = {0};

    device_get_mesh_addr(mesh_id);

    mlink_json_pack(&handle_data->resp_data, "mesh_id", mlink_mac_hex2str(mesh_id, temp_str));

    strcpy(temp_str, "");
    device_get_default_netif_name(temp_str);
    if(strlen(temp_str) > 0) {
        mlink_json_pack(&handle_data->resp_data, "default_netif", temp_str);
    }

    mlink_json_pack(&handle_data->resp_data, "free_heap", esp_get_free_heap_size());
    mlink_json_pack(&handle_data->resp_data, "running_time", xTaskGetTickCount() * (1000 / configTICK_RATE_HZ));

    handle_data->resp_size = strlen(handle_data->resp_data);

    return MDF_OK;
}

static mdf_err_t mlink_handle_set_info(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret = ESP_OK;
    uint8_t mesh_id[6]      = {0};
    char temp_str[32]         = {0};
	int data;
	
    if (mlink_json_parse(handle_data->req_data, "mesh_id", temp_str) == ESP_OK) {
        mlink_mac_str2hex(temp_str, mesh_id);
		device_set_mesh_addr(mesh_id);
    }

    if (mlink_json_parse(handle_data->req_data, "default_netif", temp_str) == ESP_OK) {
		device_set_default_netif_name(temp_str);
    }

    if (mlink_json_parse(handle_data->req_data, "log_level", &data) == ESP_OK) {
        esp_log_level_set("*", data);
        MDF_LOGI("Set log level: %d", data);
    }

    if(mlink_json_parse(handle_data->req_data, "name", temp_str) == ESP_OK) {
        ret = mlink_device_set_name(temp_str);
        MDF_ERROR_CHECK(ret < 0, ret, "device_config_set");

        MDF_LOGD("Set device name: %s", temp_str);
    }

    if(mlink_json_parse(handle_data->req_data, "sn", temp_str) == ESP_OK) {
        device_set_sn(temp_str);
        MDF_LOGD("Set device sn: %s", temp_str);
    }

    return MDF_OK;
}

static mdf_err_t mlink_handle_set_config(mlink_handle_data_t *handle_data)
{
    mdf_err_t ret = ESP_OK;
    uint8_t mesh_id[6]      = {0};
    char temp_str[17]         = {0};
	int data;
	
    if (mlink_json_parse(handle_data->req_data, "mesh_id", temp_str) == ESP_OK) {
        mlink_mac_str2hex(temp_str, mesh_id);
		device_set_mesh_addr(mesh_id);
    }

    if (mlink_json_parse(handle_data->req_data, "default_netif", temp_str) == ESP_OK) {
		device_set_default_netif_name(temp_str);
    }

    if (mlink_json_parse(handle_data->req_data, "log_level", &data) == ESP_OK) {
        esp_log_level_set("*", data);
        MDF_LOGI("Set log level: %d", data);
    }

    return ESP_OK;
}

/**
 * @brief iBeacon's data format
 */
// typedef struct {
//     uint8_t flags[3];
//     uint8_t length;
//     uint8_t type;
//     uint16_t company_id;
//     uint16_t beacon_type;
//     uint8_t proximity_uuid[16];
//     uint16_t major;
//     uint16_t minor;
//     int8_t measured_power;
// } __attribute__((packed)) mlink_ble_ibeacon_t;

// mdf_err_t mlink_ble_ibeacon_set_config(mlink_handle_data_t *handle_data)
// {
//     /* Major and Minor part are stored in big endian mode in iBeacon packet,
//      * need to use this macro to transfer while creating or processing
//      * iBeacon data */
// #define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8)|(((x)&0xFF)<<8))

//     mdf_err_t ret        = ESP_OK;
//     char device_name[32] = {0};
//     char uuid_str[33]    = {0};
//     mlink_ble_config_t config = {0x0};

//     /**
//      * @brief for iBeacon packet format, please refer to Apple "Proximity Beacon Specification" doc,
//      *  https://developer.apple.com/ibeacon/Getting-Started-with-iBeacon.pdf*/
//     /* constant part of iBeacon data */
//     mlink_ble_ibeacon_t *ibeacon_adv_data = (mlink_ble_ibeacon_t *)config.custom_data;

//     ret = mlink_ble_get_config(&config);
//     MDF_ERROR_CHECK(ret != MDF_OK, ret, "mlink_ble_get_config");

//     mlink_json_parse(handle_data->req_data, "name", device_name);
//     mlink_json_parse(handle_data->req_data, "major", &ibeacon_adv_data->major);
//     mlink_json_parse(handle_data->req_data, "minor", &ibeacon_adv_data->minor);
//     mlink_json_parse(handle_data->req_data, "power", &ibeacon_adv_data->measured_power);

//     if (mlink_json_parse(handle_data->req_data, "uuid", uuid_str) == MDF_OK) {
//         uint8_t uuid[20] = {0x0};

//         for (int i = 0; i < strlen(uuid_str) && i < 16; ++i) {
//             sscanf(uuid_str + i * 2, "%02x", (int *)(uuid + i));
//         }

//         memcpy(ibeacon_adv_data->proximity_uuid, uuid, sizeof(ibeacon_adv_data->proximity_uuid));
//     }

// #pragma GCC diagnostic push
// #pragma GCC diagnostic ignored "-Wstringop-truncation"
//     strncpy(config.name, device_name, sizeof(config.name) - 1);
// #pragma GCC diagnostic pop
//     config.custom_size = sizeof(mlink_ble_ibeacon_t);

//     ret = mlink_ble_set_config(&config);
//     MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "mlink_ble_set_config, ret: %d", ret);

//     return ESP_OK;
// }

// mdf_err_t mlink_ble_ibeacon_get_config(mlink_handle_data_t *handle_data)
// {
//     mdf_err_t ret             = ESP_OK;
//     char uuid_str[33]         = {0};
//     mlink_ble_config_t config = {0x0};

//     ret = mlink_ble_get_config(&config);
//     MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "mlink_ble_set_config, ret: %d", ret);

//     mlink_ble_ibeacon_t *ibeacon_adv_data = (mlink_ble_ibeacon_t *)config.custom_data;

//     for (int i = 0; i < 16; ++i) {
//         sprintf(uuid_str + i * 2, "%02x", ibeacon_adv_data->proximity_uuid[i]);
//     }

//     mlink_json_pack(&handle_data->resp_data, "name", config.name);
//     mlink_json_pack(&handle_data->resp_data, "uuid", uuid_str);
//     mlink_json_pack(&handle_data->resp_data, "major", ibeacon_adv_data->major);
//     mlink_json_pack(&handle_data->resp_data, "minor", ibeacon_adv_data->minor);
//     mlink_json_pack(&handle_data->resp_data, "power", ibeacon_adv_data->measured_power);

//     handle_data->resp_size = strlen(handle_data->resp_data);

//     return ESP_OK;
// }

// static mdf_err_t mlink_sniffer_get_data(mlink_handle_data_t *handle_data)
// {
//     mdf_err_t ret = MDF_OK;

//     handle_data->resp_fromat = MLINK_HTTPD_FORMAT_HEX;
//     ret = mlink_sniffer_data((uint8_t **)&handle_data->resp_data, (size_t *)&handle_data->resp_size);
//     MDF_ERROR_CHECK(ret != MDF_OK, ret, "get_sniffer_list, size: %d", handle_data->resp_size);

//     ESP_LOG_BUFFER_HEX_LEVEL(TAG, handle_data->resp_data, handle_data->resp_size, ESP_LOG_DEBUG);

//     return ESP_OK;
// }

// static mdf_err_t mlink_sniffer_set_cfg(mlink_handle_data_t *handle_data)
// {
//     mdf_err_t ret = MDF_OK;
//     mlink_sniffer_config_t config = {0x0};

//     mlink_sniffer_get_config(&config);

//     mlink_json_parse(handle_data->req_data, "type", &config.enable_type);
//     mlink_json_parse(handle_data->req_data, "notice_threshold", &config.notice_percentage);
//     mlink_json_parse(handle_data->req_data, "esp_module_filter", &config.esp_filter);
//     mlink_json_parse(handle_data->req_data, "ble_scan_interval", &config.ble_scan_interval);
//     mlink_json_parse(handle_data->req_data, "ble_scan_window", &config.ble_scan_window);

//     mlink_sniffer_set_config(&config);

//     if (config.enable_type == MLINK_SNIFFER_NONE) {
//         mlink_sniffer_deinit();
//     } else {
//         mlink_sniffer_init();
//     }

//     if (config.enable_type & MLINK_SNIFFER_WIFI) {
//         ret = mlink_sniffer_wifi_start();
//     } else {
//         ret = mlink_sniffer_wifi_stop();
//     }

// #ifdef CONFIG_IDF_TARGET_ESP32

//     if (config.enable_type & MLINK_SNIFFER_BLE) {
//         ret = mlink_sniffer_ble_start();
//     } else {
//         ret = mlink_sniffer_ble_stop();
//     }

// #endif

//     return ret;
// }

// static mdf_err_t mlink_sniffer_get_cfg(mlink_handle_data_t *handle_data)
// {
//     mlink_sniffer_config_t config = {0x0};
//     mlink_sniffer_get_config(&config);

//     mlink_json_pack(&handle_data->resp_data, "type", (uint8_t)config.enable_type);
//     mlink_json_pack(&handle_data->resp_data, "notice_threshold", config.notice_percentage);
//     mlink_json_pack(&handle_data->resp_data, "esp_module_filter", config.esp_filter);
//     mlink_json_pack(&handle_data->resp_data, "ble_scan_interval", config.ble_scan_interval);
//     mlink_json_pack(&handle_data->resp_data, "ble_scan_window", config.ble_scan_window);

//     handle_data->resp_size = strlen(handle_data->resp_data);

//     return ESP_OK;
// }

static mlink_handle_t g_handles_list[MLINK_HANDLES_MAX_SIZE] = {
    {"reset",            mlink_handle_system_reset},
    {"reboot",           mlink_handle_system_reboot},
    {"get_device_info",  mlink_handle_get_info},
    {"set_device_info",  mlink_handle_set_info},
    {"get_status",       mlink_handle_get_status},
    {"set_status",       mlink_handle_set_status},
    {"rename_device",    mlink_handle_set_name},
    {"delay_exec",       mlink_handle_delay_exec},
    {"set_position",     mlink_handle_set_position},
    {"set_mesh_config",  mlink_handle_set_config},
    {"get_mesh_config",  mlink_handle_get_config},
    {"get_ota_progress", mlink_handle_get_ota_progress},
    {"set_ota_fallback", mlink_handle_set_ota_fallback},
    // {"get_sniffer_info", mlink_sniffer_get_data},
    // {"get_sniffer_config", mlink_sniffer_get_cfg},
    // {"set_sniffer_config", mlink_sniffer_set_cfg},
    // {"get_ibeacon_config", mlink_ble_ibeacon_get_config},
    // {"set_ibeacon_config", mlink_ble_ibeacon_set_config},
    {NULL,                 NULL},
};

mdf_err_t mlink_set_handle(const char *name, const mlink_handle_func_t func)
{
    MDF_PARAM_CHECK(name);
    MDF_PARAM_CHECK(func);

    int i = 0;

    for (i = 0; g_handles_list[i].name && i < MLINK_HANDLES_MAX_SIZE; i++) {
        if (!strcasecmp(g_handles_list[i].name, name)) {
            g_handles_list[i].func = (mlink_handle_func_t)func;
            return ESP_OK;
        }
    }

    MDF_ERROR_CHECK(i == MLINK_HANDLES_MAX_SIZE, MDF_FAIL, "Mlink handles list is full");

    g_handles_list[i].name = name;
    g_handles_list[i].func = (mlink_handle_func_t)func;

    return ESP_OK;
}

// mdf_err_t mlink_handle(const uint8_t *src_addr, const mlink_httpd_type_t *type,
//                        const void *data, size_t size)
// {
//     MDF_PARAM_CHECK(src_addr);
//     MDF_PARAM_CHECK(type);
//     MDF_PARAM_CHECK(data);
//     MDF_PARAM_CHECK(size > 0);

//     mdf_err_t ret                = MDF_FAIL;
//     const uint8_t *dest_addr     = NULL;
//     char func_name[32]           = {0x0};
//     mlink_httpd_type_t resp_type = {0x0};
//     mwifi_data_type_t data_type  = {
//         .compression = true,
//     };
//     mlink_handle_data_t handle_data = {
//         .req_data    = data,
//         .req_size    = size,
//         .req_fromat  = MLINK_HTTPD_FORMAT_JSON,
//         .resp_data   = NULL,
//         .resp_size   = 0,
//         .resp_fromat = MLINK_HTTPD_FORMAT_JSON,
//     };

//     MDF_ERROR_GOTO(type->format != MLINK_HTTPD_FORMAT_JSON, EXIT,
//                    "The current version only supports the json protocol");

//     ret = mlink_json_parse(handle_data.req_data, "request", func_name);
//     MDF_ERROR_GOTO(ret != MDF_OK, EXIT, "mlink_json_parse, ret: %d, key: %s, value: %.*s",
//                    ret, func_name, handle_data.req_size, handle_data.req_data);

//     ret = MDF_ERR_NOT_SUPPORTED;

//     /**< If we can find this request from our list, we will handle this request */
//     for (int i = 0; g_handles_list[i].func; i++) {
//         if (!strcasecmp(func_name, g_handles_list[i].name)) {
//             MDF_LOGD("Function: %s", func_name);
//             ret = g_handles_list[i].func(&handle_data);
//             break;
//         }
//     }

//     /**< Check flag to decide whether reponse */
//     if (!type->resp) {
//         return MDF_OK;
//     }

//     if (type->from != MLINK_HTTPD_FROM_SERVER) {
//         dest_addr = src_addr;
//     }

// EXIT:

//     resp_type.sockfd = type->sockfd;
//     resp_type.format = handle_data.resp_fromat;
//     resp_type.from   = MLINK_HTTPD_FROM_DEVICE;
//     resp_type.resp   = (ret == MDF_OK) ? true : false;
//     data_type.protocol = MLINK_PROTO_HTTPD;
//     memcpy(&data_type.custom, &resp_type, sizeof(mlink_httpd_type_t));

//     if (handle_data.resp_fromat == MLINK_HTTPD_FORMAT_JSON) {
//         mlink_json_pack(&handle_data.resp_data, "status_msg", mdf_err_to_name(ret));
//         handle_data.resp_size = mlink_json_pack(&handle_data.resp_data, "status_code", -ret);
//     }

//     ret = mwifi_write(dest_addr, &data_type, handle_data.resp_data, handle_data.resp_size, true);
//     MDF_LOGD("resp_size: %d, resp: %.*s", handle_data.resp_size, handle_data.resp_size, handle_data.resp_data);
//     MDF_FREE(handle_data.resp_data);
//     MDF_ERROR_CHECK(ret != ESP_OK, ret, "mdf_write");

//     return MDF_OK;
// }

mdf_err_t mlink_handle_request(mlink_handle_data_t *handle_data)
{
    MDF_PARAM_CHECK(handle_data);

    mdf_err_t ret           = MDF_FAIL;
    char      func_name[32] = {0x0};

    ret = mlink_json_parse(handle_data->req_data, "request", func_name);
    MDF_ERROR_CHECK(ret != MDF_OK, ret, "mlink_json_parse, ret: %d, key: %s, value: %.*s",
                    ret, func_name, handle_data->req_size, handle_data->req_data);

    ret = MDF_ERR_NOT_SUPPORTED;

    /**< If we can find this request from our list, we will handle this request */
    for (int i = 0; g_handles_list[i].func; i++) {
        if (!strcasecmp(func_name, g_handles_list[i].name)) {
            MDF_LOGD("Function: %s", func_name);
            ret = g_handles_list[i].func(handle_data);
        }
    }

    return ret;
}


mdf_err_t mlink_sync_request(char trigger_cid[32], char dest_cid[32], const uint8_t *addrs_list, size_t addrs_num)
{
    mdf_err_t ret   = MDF_OK;
	char *execute_content=NULL;
    characteristic_value_t value_union = {.value_int = -1};

	characteristic_format_t format = mlink_get_characteristics_format(trigger_cid);
	ret = mlink_device_get_value(trigger_cid, &value_union);
	MDF_ERROR_CHECK(ret < 0, ret, "<%s> mlink_device_get_value, cid: %s", mdf_err_to_name(ret), trigger_cid);

	if(format == CHARACTERISTIC_FORMAT_DOUBLE){
		asprintf(&execute_content, "{\"request\":\"set_status\",\"characteristics\":[{\"cid\":%s,\"value\":%.3f}]}",
				 dest_cid, (float)value_union.value_float);
	}else{
		asprintf(&execute_content, "{\"request\":\"set_status\",\"characteristics\":[{\"cid\":%s,\"value\":%d}]}",
				 dest_cid, value_union.value_int);
	}

	/* send request */
	ret = mlink_espnow_write(addrs_list, addrs_num, execute_content,
						  strlen(execute_content), MLINK_ESPNOW_COMMUNICATE_UNICAST, portMAX_DELAY);
	if(execute_content){
		free(execute_content);
	}
    return ret;
}

QueueHandle_t g_mlink_handle_queue     = NULL;
mdf_err_t mlink_handle_request_read(mlink_handle_data_t **request, TickType_t wait_ticks)
{
    MDF_PARAM_CHECK(request);

    mdf_err_t ret = MDF_OK;

    if (!g_mlink_handle_queue) {
        g_mlink_handle_queue = xQueueCreate(5, sizeof(mlink_handle_data_t *));
    }

    ret = xQueueReceive(g_mlink_handle_queue, request, wait_ticks);
    MDF_ERROR_CHECK(ret != true, MDF_ERR_TIMEOUT, "xQueueSend failed");

    return MDF_OK;
}
